package net.chanibal.hala.server;

import net.chanibal.hala.model.*;

import java.sql.Connection;
import java.util.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpSession;

import org.apache.torque.NoRowsException;
import org.apache.torque.TooManyRowsException;
import org.apache.torque.Torque;
import org.apache.torque.TorqueException;

import net.chanibal.hala.client.Server;
import net.chanibal.hala.client.Server.TerminData;

import org.apache.torque.util.Criteria;
import org.apache.torque.util.Transaction;

import sun.misc.Compare;
import sun.misc.Sort;

import net.chanibal.hala.model.Impreza;
import net.chanibal.hala.model.ImprezaPeer;
import net.chanibal.hala.model.Termin;
import net.chanibal.hala.model.TerminPeer;

import com.google.gwt.user.client.rpc.SerializableException;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class ServerImpl extends RemoteServiceServlet implements Server {

	static {
		try {
			Torque.init("torque-3.3/Torque.properties");
		} catch (TorqueException e) {
			e.printStackTrace();
		}
	}

	private static final long serialVersionUID = 1L;
	public static final String LOGIN_NAME_KEY = "LOGIN_NAME";
	public static final String ACCOUNT_TYPE = "ACCOUNT_TYPE";
	public static final String ACCOUNT_TYPE_ADMIN = "ACCOUNT_TYPE_ADMIN";
	public static final String ACCOUNT_TYPE_CLIENT = "ACCOUNT_TYPE_CLIENT";

	private HttpSession getSession() {
		HttpSession session = this.getThreadLocalRequest().getSession();
		return session;
	}

	@Override
	public String getUserName() {
		return (String) getSession().getAttribute(LOGIN_NAME_KEY);
	}

	@Override
	public Boolean isAdmin() {
		return getSession().getAttribute(ACCOUNT_TYPE).equals(
				ACCOUNT_TYPE_ADMIN);
	}

	@Override
	public Boolean isValid() {
		return getSession().getAttribute(LOGIN_NAME_KEY) != null;
	}

	private void requireValid() throws SerializableException {
		if (!isValid())
			throw new SerializableException("Akcja wymaga zalogowania się");
	}

	private void requireAdmin() throws SerializableException {
		if (!isAdmin())
			throw new SerializableException(
					"Akcja wymaga uprawnień administratora");
	}

	// === z diagramu UCD ===

	@Override
	public void doAddImpreza(String tytul, Float kosztZaTermin, Integer miejsc,
			Date[][] terminy) throws SerializableException {
		requireAdmin();
		Connection conn = null;
		try {
			conn = Transaction.begin(ImprezaPeer.DATABASE_NAME);
			Impreza impreza = new Impreza();
			impreza.setTytul(tytul);
			impreza.setCena(kosztZaTermin);
			impreza.setMiejsc(miejsc);
			impreza.save(conn);
			final int ROZPOCZECIE = 0;
			final int ZAKONCZENIE = 1;
			for (Date[] daty : terminy) {
				if (daty[ROZPOCZECIE].after(daty[ZAKONCZENIE]))
					throw new Exception(
							"Niepoprawne daty - rozpoczęcie po zakończeniu?");
				if (daty[ROZPOCZECIE].before(new Date()))
					throw new Exception(
							"Niepoprawne daty - rozpoczęcie w przeszłości?");
				Criteria c = new Criteria();
				// select count(*) from termin where :zakonczenie > rozpoczecie
				// and :rozpoczecie < zakonczenie;
				c.add(TerminPeer.ROZPOCZECIE, daty[ZAKONCZENIE],
						Criteria.GREATER_THAN);
				c.add(TerminPeer.ZAKONCZENIE, daty[ROZPOCZECIE],
						Criteria.LESS_THAN);
				if (TerminPeer.doSelect(c).size() > 0)
					throw new Exception(
							"Niestety, co najmniej jeden z terminów jest już zajęty");
				Termin termin = new Termin();
				termin.setRozpoczecie(daty[ROZPOCZECIE]);
				termin.setZakonczenie(daty[ZAKONCZENIE]);
				termin.setImpreza(impreza);
				termin.save(conn);
			}
			Transaction.commit(conn);
		} catch (Exception e) {
			e.printStackTrace();
			if (conn != null)
				try {
					Transaction.rollback(conn);
				} catch (TorqueException e1) {
					e.printStackTrace();
					throw new SerializableException("" + e);
				}
			throw new SerializableException("" + e);
		}
	}

	@Override
	public void doAdminLogin(String pass) throws SerializableException {
		// / FIXME hasło w pliczek tekstowy (properties?)
		if (pass.equals("administrator")) {
			getSession().setAttribute(LOGIN_NAME_KEY, "(administrator)");
			getSession().setAttribute(ACCOUNT_TYPE, ACCOUNT_TYPE_ADMIN);
		} else
			throw new SerializableException("niepoprawne hasło administratora");
	}

	@SuppressWarnings("unchecked")
	@Override
	public void doLogin(String user, String pass) throws SerializableException {
		try {
			Criteria c = new Criteria();
			c.add(KlientPeer.LOGIN, user);
			List<Klient> Klienti;
			Klienti = KlientPeer.doSelect(c);
			if (Klienti.size() == 0)
				throw new SerializableException("Nie ma takiego użytkownika");
			else if (Klienti.get(0).getPass().equals(pass)) {
				getSession().setAttribute(LOGIN_NAME_KEY, user);
				getSession().setAttribute(ACCOUNT_TYPE, ACCOUNT_TYPE_CLIENT);
			} else
				throw new SerializableException("Niepoprawne hasło.");
		} catch (TorqueException e) {
			e.printStackTrace();
			throw new SerializableException(e.toString());
		}
	}

	@Override
	public void doLogout() throws SerializableException {
		requireValid();
		getSession().removeAttribute(LOGIN_NAME_KEY);
		getSession().removeAttribute(ACCOUNT_TYPE);
	}

	@Override
	public void doRegister(String login, String pass, Integer age, char sex,
			String address, String name, String familyName)
			throws SerializableException {
		try {
			Klient klient = new Klient();
			klient.setLogin(login);
			klient.setPass(pass);
			klient.setYear(age);
			if (sex != 'K' && sex != 'M')
				throw new SerializableException("Płeć musi być K lub M");
			klient.setSex(String.valueOf(sex));
			klient.setAdres(address);
			klient.setName(name);
			klient.setFamilyname(familyName);
			klient.setBill(0.0);
			klient.save();
		} catch (TorqueException e) {
			throw new SerializableException("Baza danych:" + e.getMessage());
		} catch (Exception e) {
			throw new SerializableException("Bład zapisu do bazy");
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void doReserve(Integer imprezaId) throws SerializableException {
		requireValid();
		System.out.println(imprezaId);
		try {
			Connection conn = Transaction.begin(ImprezaPeer.DATABASE_NAME);
			try {
				Impreza impreza = ImprezaPeer.retrieveByPK(imprezaId);
				Klient klient = KlientPeer.getKlientByLogin(getUserName());
				Criteria c = new Criteria();
				c.add(TerminPeer.ROZPOCZECIE, new Date(),
						Criteria.GREATER_EQUAL);
				// terminy od teraz
				List<Termin> terminy = TerminPeer.doSelect(c);
				Double doZaplaty = 0.0;
				for (Termin termin : terminy) {
					if (termin.getKlientTermins().size() > termin.getImpreza()
							.getMiejsc())
						throw new Exception(
								"Zabrakło miejsc na co najmniej jeden z terminów");
					KlientTermin kt = new KlientTermin();
					kt.setKlient(klient);
					kt.setTermin(termin);
					kt.save(conn);
					doZaplaty += impreza.getCena();
				}
				klient.setBill(klient.getBill() + doZaplaty);
				klient.save(conn);
				Transaction.commit(conn);
			} catch (NoRowsException e) {
				Transaction.rollback(conn);
				throw new SerializableException("Nie ma takiej imprezy");
			} catch (Exception e) {
				Transaction.rollback(conn);
				throw e;
			}
		} catch (Exception e1) {
			e1.printStackTrace();
			throw new SerializableException(e1.toString());
		}
	}

	@Override
	public void doWplata(String klientLogin, Double ile)
			throws SerializableException {
		requireAdmin();
		Klient klient;
		try {
			klient = KlientPeer.getKlientByLogin(klientLogin);
			klient.setBill(klient.getBill() - ile);
			klient.save();
		} catch (Exception e) {
			e.printStackTrace();
			throw new SerializableException("Nie ma takiego klienta");
		}
	}

	@Override
	public Double getBilling() throws SerializableException {
		try {
			return KlientPeer.getKlientByLogin(getUserName()).getBill();
		} catch (Exception e) {
			e.printStackTrace();
			throw new SerializableException("Nie ma takiego klienta");
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public String[][] getImprezy() throws SerializableException {
		try {
			Criteria c2 = new Criteria();
			c2.addDescendingOrderByColumn(ImprezaPeer.ID);
			List<Impreza> imprezy = ImprezaPeer.doSelect(c2);
			List<String[]> ret = new ArrayList<String[]>();
			for (Impreza imp : imprezy) {
				Criteria c = new Criteria();
				c.addAscendingOrderByColumn(TerminPeer.ROZPOCZECIE);
				c.add(TerminPeer.ROZPOCZECIE, new Date(),
						Criteria.GREATER_EQUAL);
				List<Termin> terminy = imp.getTermins(c);
				String nastTermin;
				try {
					nastTermin = terminy.get(0).getRozpoczecie().toString();
				} catch (Exception e) {
					// nastTermin = "-";
					continue;
				}
				String[] temp = { imp.getTytul(), nastTermin,
						String.valueOf(terminy.size()),
						String.valueOf(imp.getMiejsc()),
						String.valueOf(imp.getId()) };
				ret.add(temp);
			}
			String[][] r = ret.toArray(new String[ret.size()][5]);
			Sort.quicksort(r, new Compare() {

				@Override
				public int doCompare(Object arg0, Object arg1) {
					// if(Integer.parseInt(String[])arg0)[2])<=0)return -1;
					// if(Integer.parseInt(String[])arg1)[2])<=0)return 1;
					return ((String[]) arg0)[1].compareTo(((String[]) arg1)[1]);
				}
			});
			return r;
		} catch (TorqueException e) {
			e.printStackTrace();
			throw new SerializableException(e.toString());
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public String[][] getKlienci() throws SerializableException {
		requireAdmin();
		ArrayList<String[]> ret = new ArrayList<String[]>();
		List<Klient> klienci;
		try {
			klienci = KlientPeer.doSelect(new Criteria());
			for (Klient klient : klienci) {
				String[] t = new String[3];
				t[0] = klient.getLogin();
				t[1] = klient.getName() + " " + klient.getFamilyname();
				t[2] = String.valueOf(klient.getBill());
				ret.add(t);
			}

		} catch (TorqueException e) {
			e.printStackTrace();
			throw new SerializableException(e.toString());
		}
		return ret.toArray(new String[ret.size()][5]);
	}

	@SuppressWarnings("unchecked")
	@Override
	public KlientData getKlient(String klientLogin) throws SerializableException {
		if (klientLogin == null)
			klientLogin = getUserName();
		if (klientLogin == null)
			return null;
		System.out.println("Pobieranie:" + klientLogin);
		KlientData ret = new KlientData();
		Klient klient;
		try {
			klient = KlientPeer.getKlientByLogin(klientLogin);
		} catch (NoRowsException e) {
			throw new SerializableException("Nie ma takiego klienta");
		} catch (TooManyRowsException e) {
			throw new SerializableException(e.toString());
		} catch (TorqueException e) {
			throw new SerializableException(e.toString());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new SerializableException(e.getMessage());
		}
		ret.name = klient.getName();
		ret.familyName = klient.getFamilyname();
		ret.sex = klient.getSex();
		ret.bill = klient.getBill();
		ret.age = klient.getYear();
		ret.address = klient.getAdres();
		ret.terminy = new TerminData[0];
//		try {
//			List<Termin> terminy = klient.getKlientTermins();
//			ret.terminy = new TerminData[terminy.size()];
//			for (int x = 0; x < terminy.size(); x++) {
//				TerminData td = new TerminData();
//				Termin t = terminy.get(x);
//				td.rozpoczecie = t.getRozpoczecie();
//				td.zakonczenie = t.getZakonczenie();
//				td.nazwaImprezy = t.getImpreza().getTytul();
//				td.idTerminu = t.getId();
//				td.idImprezy = t.getImpreza().getId();
//				ret.terminy[x] = td;
//			}
//		} catch (TorqueException e) {
//			// TODO Auto-generated catch block
//			throw new SerializableException(e.toString());
//		}
		return ret;
	}

	
	public ImprezaData getImpreza(Integer id) throws SerializableException {
		try {
			Impreza imp=ImprezaPeer.retrieveByPK(id);
			ImprezaData d=new ImprezaData();
			d.kosztZaTermin=imp.getCena();
			d.miejsc=imp.getMiejsc();
			d.tytul=imp.getTytul();
			d.miejscWolnych=((Termin)imp.getTermins().get(0)).getKlientTermins().size();
			return d;
		} catch (Exception e) {
			e.printStackTrace();
			throw new SerializableException("Błąd "+e.toString());
		}
//		catch (NoRowsException e) {
//			throw new SerializableException("nie ma takiej imprezy w bazie");
//		} catch (TooManyRowsException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//			throw new SerializableException("nie ma takiej imprezy w bazie");
//		} catch (TorqueException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//			throw new SerializableException("nie ma takiej imprezy w bazie");
//		}
		

	}

	public String[][] getImprezaTerminy(Integer id)
			throws SerializableException {
		Impreza imp;
		try {
			imp = ImprezaPeer.retrieveByPK(id);
			List<Termin> terminy=imp.getTermins();
			String[][] ret = new String[terminy.size()][2];
			for(int x=0;x<terminy.size();x++) {
				Termin t = terminy.get(x);
				TerminData td=new TerminData();
				ret[x][0]=t.getRozpoczecie().toLocaleString();
				ret[x][1]=t.getZakonczenie().toLocaleString();
			}
			return ret;
		} catch (Exception e) {
			e.printStackTrace();
			throw new SerializableException("Błąd "+e.toString());
		}

	}

}